﻿using System;
using System.Collections;
using System.Linq;
using System.Linq.Expressions;
using Microsoft.SharePoint;
using Sapphire.Linq.FieldMaps;

namespace Sapphire.Linq.Framework
{
    public class LinqQueryProvider : QueryProvider, IDisposable, ICustomPropertyMaps
    {
        public SPSite Site { get; private set; }

        public SPWeb Web { get; private set; }

        protected SPMetadataCache MetadataCache = new SPMetadataCache();

        private readonly bool _needDispose;
        private IPropertyMapsContext _mapsContext;

        public ListQuery<T> GetList<T>(string listUrl)
        {
            if (!MetadataCache.ContainsList(typeof(T), listUrl))
                MetadataCache.AddList(typeof(T), SPData.GetList(Web, listUrl));

            return new ListQuery<T>(this, MetadataCache.GetList(typeof(T), listUrl));
        }

        public ListQuery<T> GetList<T>(Guid listId)
        {
            if (!MetadataCache.ContainsList(typeof(T), listId))
                MetadataCache.AddList(typeof(T), SPData.GetList(Web, listId));

            return new ListQuery<T>(this, MetadataCache.GetList(typeof(T), listId));
        }

        private void Initialize(SPSite site, SPWeb web)
        {
            Site = site;
            Web = web;
            InitMaps(_mapsContext = new MapsContext());
        }

        public LinqQueryProvider(string url)
        {
            _needDispose = true;
            var site = new SPSite(url);
            var web = site.OpenWeb();
            Initialize(site, web);
        }

        public LinqQueryProvider(SPWeb web)
        {
            Initialize(web.Site, web);
        }

        public T Execute<T>(Expression expression, SPList list)
        {
            if (expression.IsEmptyCount())
            {
                return (T)(object)list.ItemCount;
            }

            SPQuery query = CamlExpressionVisitor.Create(_mapsContext).Parse(expression);

            if (expression.IsSkip())
            {
                SPQuery skipQuery = CamlExpressionVisitor.Create(_mapsContext).ParseSkipQuery(expression);
                skipQuery.ViewFields = "<FieldRef Name='ID' />";
                var skipItems = SPData.GetItems(list, skipQuery);
                query.ListItemCollectionPosition = skipItems.ListItemCollectionPosition;
            }

            var items = SPData.GetItems(list, query);

            if (expression.Type.GetInterface(typeof(IEnumerable).Name) != null)
            {
                return Mapper.Instance.Map<T>(items.Cast<SPListItem>(), this);
            }

            if (expression.IsCount())
            {
                return (T)(object)items.Count;
            }

            return Mapper.Instance.Map<T>(items.Cast<SPListItem>().FirstOrDefault(), this);
        }

        public override object Execute(Expression expression)
        {
            throw new NotImplementedException();
        }

        public override T Execute<T>(Expression expression)
        {
            return Execute<T>(expression, ExpressionHelper.GetList(expression));
        }

        public override IQueryable<T> CreateQuery<T>(Expression expression)
        {
            if (expression == null)
            {
                throw new ArgumentException("expression");
            }

            return new ListQuery<T>(this, expression);
        }

        public virtual void InitMaps(IPropertyMapsContext mapsContext)
        {
        }

        public IPropertyMapsContext MapsContext
        {
            get { return _mapsContext; }
        }

        public void Dispose()
        {
            Mapper.Instance.Clear();

            if (!_needDispose) return;

            if (Web != null)
                Web.Dispose();

            if (Site != null)
                Site.Dispose();
        }
    }
}